package com.starmark.gateway.springcloud.provider.service.impl;

import com.alibaba.fastjson.JSON;
import com.starmark.gateway.springcloud.provider.constants.GatewayConstant;
import com.starmark.gateway.springcloud.provider.dto.GatewayAppDto;
import com.starmark.gateway.springcloud.provider.dto.GatewayProjectDto;
import com.starmark.gateway.springcloud.provider.service.ICacheService;
import com.starmark.gateway.springcloud.provider.service.IRouteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.CorsConfiguration;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Slf4j
public class RouteServiceImpl implements IRouteService {


    @Autowired
    private ICacheService cacheService;


    private Map<String, GatewayProjectDto> gatewayProjectMap = new ConcurrentHashMap<>();

    private Map<String, RouteDefinition> routeDefinitionMaps = new ConcurrentHashMap<>();


    private void loadGatewayProjects() {
        //先在redis获取所有工程

        Set<String> gatewayProjectKeys = cacheService.getCacheKeysByKey(GatewayConstant.STARMARK_GATEWAY_PROJECT + "*");
        log.info("redis key to {}", gatewayProjectKeys);
        if (CollectionUtils.isEmpty(gatewayProjectKeys)) {
            return;
        }
        List<String> gatewayProjects = Optional.ofNullable(cacheService.getCacheListByKeys(gatewayProjectKeys))
                .orElse(Collections.emptyList());
        gatewayProjects.forEach(value -> {
            GatewayProjectDto gatewayProjectDto = JSON.parseObject(JSON.parse(value).toString(), GatewayProjectDto.class);
            gatewayProjectMap.put(gatewayProjectDto.getProjectCode(), gatewayProjectDto);
        });

    }


    private void loadRouteDefinition() {
        //遍历工程,获取所有APP
        gatewayProjectMap.values().forEach(gatewayProject -> {
            Set<String> gatewayRouteKeys = cacheService.getCacheKeysByKey(GatewayConstant.STARMARK_GATEWAY_ROUTES + gatewayProject.getProjectCode() + ":*");
            log.info("gatewayRouteKeys {}", gatewayRouteKeys);
            if (!CollectionUtils.isEmpty(gatewayRouteKeys)) {
                List<String> gatewayRouteList = Optional.ofNullable(cacheService.getCacheListByKeys(gatewayRouteKeys))
                        .orElse(Collections.emptyList());
                log.info("gatewayRouteList {}", gatewayRouteList);
                gatewayRouteList.forEach(value -> {
                    RouteDefinition routeDefinition =
                            JSON.parseObject(JSON.parse(value).toString(), RouteDefinition.class);
                    routeDefinitionMaps.put(routeDefinition.getId(), routeDefinition);

                });
            }
        });
    }


    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        if (gatewayProjectMap.isEmpty()) {
            loadGatewayProjects();
            loadRouteDefinition();
        }
        return Flux.fromIterable(routeDefinitionMaps.values());
    }


    @Override
    public Mono<Void> save(Mono<RouteDefinition> routeDefinitionMono) {
        return routeDefinitionMono.flatMap(routeDefinition -> {
            routeDefinitionMaps.put(routeDefinition.getId(), routeDefinition);
            return Mono.empty();
        });
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            routeDefinitionMaps.remove(id);
            return Mono.empty();
        });
    }

    @Override
    public GatewayProjectDto getGatewayProject(String projectCode) {
        return gatewayProjectMap.get(projectCode);
    }

    @Override
    public List<GatewayAppDto> getGatewayApps(String projectCode) {
        Set<String> gatewayAppKeys = cacheService.getCacheKeysByKey(GatewayConstant.STARMARK_GATEWAY_APPS + projectCode + ":*");
        log.info("gatewayAppKeys {}", gatewayAppKeys);
        if (!CollectionUtils.isEmpty(gatewayAppKeys)) {
            List<String> gatewayAppList = Optional.ofNullable(cacheService.getCacheListByKeys(gatewayAppKeys))
                    .orElse(Collections.emptyList());
            log.info("gatewayAppList {}", gatewayAppList);
            return gatewayAppList.stream().map(appKey -> JSON.parseObject(JSON.parse(appKey).toString(), GatewayAppDto.class)).collect(Collectors.toList());

        }
        return Collections.emptyList();
    }

    @Override
    public CorsConfiguration getGatewayProjectCorsConfiguration(String projectCode) {
        GatewayProjectDto project=this.getGatewayProject(projectCode);
        Assert.notNull(project,"工程不存在:"+projectCode);
        CorsConfiguration corsConfiguration=new CorsConfiguration();
          corsConfiguration.setAllowCredentials(project.getAllowCredentials());
        if(!CollectionUtils.isEmpty(project.getAllowedHeaders())) {
            corsConfiguration.setAllowedHeaders(project.getAllowedHeaders());
        }

        if(!CollectionUtils.isEmpty(project.getAllowedMethods())) {
            corsConfiguration.setAllowedMethods(project.getAllowedMethods());
        }
        if(!CollectionUtils.isEmpty(project.getExposedHeaders())) {
            corsConfiguration.setExposedHeaders(project.getExposedHeaders());
        }
        if(!CollectionUtils.isEmpty(project.getAllowedOrigins())) {
            corsConfiguration.setAllowedOrigins(project.getAllowedOrigins());
        }

        return corsConfiguration;
    }

    @Autowired
    protected ApplicationEventPublisher publisher;

    //3.添加定时任务
    @Scheduled(cron = "0 0/1 * * * ?")
    public void loadProject() {
        log.info("重新刷新缓存");
        gatewayProjectMap.clear();
        routeDefinitionMaps.clear();
        loadGatewayProjects();
        loadRouteDefinition();
        //刷新缓存
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        log.info("重新刷新缓存结束");
    }
}
